/****************************************************************************
* VCGLib                                                            o o     *
* Visual and Computer Graphics Library                            o     o   *
*                                                                _   O  _   *
* Copyright(C) 2004                                                \/)\/    *
* Visual Computing Lab                                            /\/|      *
* ISTI - Italian National Research Council                           |      *
*                                                                    \      *
* All rights reserved.                                                      *
*                                                                           *
* This program is free software; you can redistribute it and/or modify      *
* it under the terms of the GNU General Public License as published by      *
* the Free Software Foundation; either version 2 of the License, or         *
* (at your option) any later version.                                       *
*                                                                           *
* This program is distributed in the hope that it will be useful,           *
* but WITHOUT ANY WARRANTY; without even the implied warranty of            *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
* GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
* for more details.                                                         *
*                                                                           *
****************************************************************************/

#ifndef __VCG_GLTRIMESH
#define __VCG_GLTRIMESH

#include <queue>
#include <vector>

//#include <GL/glew.h>
#include <wrap/gl/space.h>
#include <wrap/gl/math.h>
#include <vcg/space/color4.h>

#include CATTOBLEPA!!

namespace vcg {


// classe base di glwrap usata solo per poter usare i vari drawmode, normalmode senza dover
// specificare tutto il tipo (a volte lunghissimo)
// della particolare classe glwrap usata.
class GLW {
public:
    enum DrawMode	{DMNone, DMBox, DMPoints, DMWire, DMHidden, DMFlat, DMSmooth, DMFlatWire, DMRadar, DMLast} ;
    enum NormalMode	{NMNone, NMPerVert, NMPerFace, NMPerWedge, NMLast};
    enum ColorMode	{CMNone, CMPerMesh, CMPerFace, CMPerVert, CMLast};
    enum TextureMode {TMNone, TMPerVert, TMPerWedge, TMPerWedgeMulti};
    enum Hint {
        HNUseTriStrip		  = 0x0001,				// ha bisogno che ci sia la fftopology gia calcolata!
//		HNUseEdgeStrip		  = 0x0002,			//
        HNUseDisplayList	  = 0x0004,
        HNCacheDisplayList	  = 0x0008,		// Each mode has its dl;
        HNLazyDisplayList	  = 0x0010,			// Display list are generated only when requested
        HNIsTwoManifold		  = 0x0020,			// There is no need to make DetachComplex before .
        HNUsePerWedgeNormal	  = 0x0040,		//
        HNHasFFTopology       = 0x0080,		// E' l'utente che si preoccupa di tenere aggiornata la topologia ff
        HNHasVFTopology       = 0x0100,		// E' l'utente che si preoccupa di tenere aggiornata la topologia vf
        HNHasVertNormal       = 0x0200,		// E' l'utente che si preoccupa di tenere aggiornata le normali per faccia
        HNHasFaceNormal       = 0x0400,		// E' l'utente che si preoccupa di tenere aggiornata le normali per vertice
        HNUseVArray           = 0x0800,
        HNUseLazyEdgeStrip	  = 0x1000,		// Edge Strip are generated only when requested
        HNUseVBO              = 0x2000,		// Use Vertex Buffer Object
        HNIsPolygonal         = 0x4000    // In wireframe modes, hide faux edges
    };

    enum Change {
        CHVertex		= 0x01,
        CHNormal		= 0x02,
        CHColor			= 0x04,
        CHFace			= 0x08,
        CHFaceNormal	= 0x10,
        CHRender        = 0x20,
        CHAll			= 0xff
    };
    enum HintParami {
        HNPDisplayListSize =0,
        HNPPointDistanceAttenuation =1,
        HNPPointSmooth = 2
    };
    enum HintParamf {
        HNPCreaseAngle =0,	// crease angle in radians
        HNPZTwist = 1,				// Z offset used in Flatwire and hiddenline modality
        HNPPointSize = 2		// the point size used in point rendering
    };

    template<class MESH_TYPE>
    class VertToSplit {
    public:
        typename MESH_TYPE::face_base_pointer f;
        char z;
        char edge;
        bool newp;
        typename MESH_TYPE::vertex_pointer v;
    };

    // GL Array Elemet
    class GLAElem {
    public :
        int glmode;
        int len;
        int start;
    };


};

template <class MESH_TYPE,  bool partial = false , class FACE_POINTER_CONTAINER = std::vector<typename MESH_TYPE::FacePointer> >
class GlTrimesh : public GLW {
public:

    typedef MESH_TYPE mesh_type;
    FACE_POINTER_CONTAINER face_pointers;


    std::vector<unsigned int> TMId;
    unsigned int array_buffers[3];

    int curr_hints;      // the current hints

    // The parameters of hints
    int   HNParami[8];
    float HNParamf[8];

    MESH_TYPE *m;
    GlTrimesh() {
        m=0;
        dl=0xffffffff;
        curr_hints=HNUseLazyEdgeStrip;
        cdm=DMNone;
        ccm=CMNone;
        cnm=NMNone;

        SetHintParamf(HNPCreaseAngle,float(M_PI/5));
        SetHintParamf(HNPZTwist,0.00005f);
        SetHintParamf(HNPPointSize,1.0f);
        SetHintParami(HNPPointDistanceAttenuation, 1);
        SetHintParami(HNPPointSmooth, 0);
    }

    ~GlTrimesh() {
        //Delete the VBOs
        if(curr_hints&HNUseVBO) {
            for(int i=0; i<3; ++i)
                if(glIsBuffer(array_buffers[i]))
                    glDeleteBuffersARB(1, (GLuint *)(array_buffers+i));
        }
    }

    void SetHintParami(const HintParami hip, const int value) {
        HNParami[hip]=value;
    }
    int GetHintParami(const HintParami hip) const {
        return HNParami[hip];
    }
    void SetHintParamf(const HintParamf hip, const float value) {
        HNParamf[hip]=value;
    }
    float GetHintParamf(const HintParamf hip) const {
        return HNParamf[hip];
    }
    void SetHint(Hint hn) {
        curr_hints |= hn;
    }
    void ClearHint(Hint hn) {
        curr_hints&=(~hn);
    }

    unsigned int dl;
    std::vector<unsigned int> indices;

    DrawMode cdm; // Current DrawMode
    NormalMode cnm; // Current NormalMode
    ColorMode ccm; // Current ColorMode

    void Update(/*Change c=CHAll*/) {
        if(m==0) return;

        if(curr_hints&HNUseVArray || curr_hints&HNUseVBO) {
            typename MESH_TYPE::FaceIterator fi;
            indices.clear();
            for(fi = m->face.begin(); fi != m->face.end(); ++fi) {
                indices.push_back((unsigned int)((*fi).V(0) - &(*m->vert.begin())));
                indices.push_back((unsigned int)((*fi).V(1) - &(*m->vert.begin())));
                indices.push_back((unsigned int)((*fi).V(2) - &(*m->vert.begin())));
            }

            if(curr_hints&HNUseVBO) {
                if(!glIsBuffer(array_buffers[1]))
                    glGenBuffers(2,(GLuint*)array_buffers);
                glBindBuffer(GL_ARRAY_BUFFER,array_buffers[0]);
                glBufferData(GL_ARRAY_BUFFER_ARB, m->vn * sizeof(typename MESH_TYPE::VertexType),
                             (char *)&(m->vert[0].P()), GL_STATIC_DRAW_ARB);

                glBindBuffer(GL_ARRAY_BUFFER,array_buffers[1]);
                glBufferData(GL_ARRAY_BUFFER_ARB, m->vn * sizeof(typename MESH_TYPE::VertexType),
                             (char *)&(m->vert[0].N()), GL_STATIC_DRAW_ARB);
            }

            glVertexPointer(3,GL_FLOAT,sizeof(typename MESH_TYPE::VertexType),0);
            glNormalPointer(GL_FLOAT,sizeof(typename MESH_TYPE::VertexType),0);
        }
    }

    void Draw(DrawMode dm ,ColorMode cm, TextureMode tm) {
        switch(dm) {
        case	DMNone    :
            Draw<DMNone    >(cm,tm);
            break;
        case	DMBox     :
            Draw<DMBox     >(cm,tm);
            break;
        case	DMPoints  :
            Draw<DMPoints  >(cm,tm);
            break;
        case	DMWire    :
            Draw<DMWire    >(cm,tm);
            break;
        case	DMHidden  :
            Draw<DMHidden  >(cm,tm);
            break;
        case	DMFlat    :
            Draw<DMFlat    >(cm,tm);
            break;
        case	DMSmooth  :
            Draw<DMSmooth  >(cm,tm);
            break;
        case	DMFlatWire:
            Draw<DMFlatWire>(cm,tm);
            break;
        default :
            break;
        }
    }

    template< DrawMode dm >
    void Draw(ColorMode cm, TextureMode tm) {
        switch(cm) {
        case	CMNone    :
            Draw<dm,CMNone   >(tm);
            break;
        case	CMPerMesh :
            Draw<dm,CMPerMesh>(tm);
            break;
        case	CMPerFace :
            Draw<dm,CMPerFace>(tm);
            break;
        case	CMPerVert :
            Draw<dm,CMPerVert>(tm);
            break;
        default :
            break;
        }
    }

    template< DrawMode dm, ColorMode cm >
    void Draw(TextureMode tm) {
        switch(tm) {
        case	TMNone          :
            Draw<dm,cm,TMNone          >();
            break;
        case	TMPerVert       :
            Draw<dm,cm,TMPerVert       >();
            break;
        case	TMPerWedge      :
            Draw<dm,cm,TMPerWedge      >();
            break;
        case	TMPerWedgeMulti :
            Draw<dm,cm,TMPerWedgeMulti >();
            break;
        default :
            break;
        }
    }



    template< DrawMode dm, ColorMode cm, TextureMode tm>
    void Draw() {
        if(!m) return;
        if((curr_hints & HNUseDisplayList)) {
            if (cdm==dm && ccm==cm) {
                glCallList(dl);
                return;
            } else {
                if(dl==0xffffffff) dl=glGenLists(1);
                glNewList(dl,GL_COMPILE);
            }
        }

        glPushMatrix();
        switch(dm) {
        case DMNone		  :
            break;
        case DMBox		  :
            DrawBBox(cm);
            break;
        case DMPoints   :
            DrawPoints<NMPerVert,cm>();
            break;
        case DMHidden		:
            DrawHidden();
            break;
        case DMFlat			:
            DrawFill<NMPerFace,cm,tm>();
            break;
        case DMFlatWire :
            DrawFlatWire<NMPerFace,cm,tm>();
            break;
        case DMRadar		:
            DrawRadar<NMPerFace,cm>();
            break;
        case DMWire		  :
            DrawWire<NMPerVert,cm>();
            break;
        case DMSmooth   :
            DrawFill<NMPerVert,cm,tm>();
            break;
        default :
            break;
        }
        glPopMatrix();

        if((curr_hints & HNUseDisplayList)) {
            cdm=dm;
            ccm=cm;
            glEndList();
            glCallList(dl);
        }
    }


    /*********************************************************************************************/
    /*********************************************************************************************/


    template <NormalMode nm, ColorMode cm, TextureMode tm>
    void DrawFill() {
#ifdef OLDMESHLAB
        if(m->fn==0) return;
        typename FACE_POINTER_CONTAINER::iterator fp;

        typename MESH_TYPE::FaceIterator fi;

        typename std::vector<typename MESH_TYPE::FaceType*>::iterator fip;
        short curtexname=-1;

        if(cm == CMPerMesh)
            glColor(m->C());

        if(tm == TMPerWedge || tm == TMPerWedgeMulti )
            glDisable(GL_TEXTURE_2D);

        if(curr_hints&HNUseVBO) {
            if( (cm==CMNone) || (cm==CMPerMesh) ) {
                if (nm==NMPerVert)
                    glEnableClientState (GL_NORMAL_ARRAY);
                glEnableClientState (GL_VERTEX_ARRAY);

                if (nm==NMPerVert) {
                    glBindBuffer(GL_ARRAY_BUFFER,array_buffers[1]);
                    glNormalPointer(GL_FLOAT,sizeof(typename MESH_TYPE::VertexType),0);
                }
                glBindBuffer(GL_ARRAY_BUFFER,array_buffers[0]);
                glVertexPointer(3,GL_FLOAT,sizeof(typename MESH_TYPE::VertexType),0);

                glDrawElements(GL_TRIANGLES ,m->fn*3,GL_UNSIGNED_INT, &(*indices.begin()) );
                glDisableClientState (GL_VERTEX_ARRAY);
                if (nm==NMPerVert)
                    glDisableClientState (GL_NORMAL_ARRAY);

                glBindBuffer(GL_ARRAY_BUFFER, 0);

                return;

            }
        }

        if(curr_hints&HNUseVArray) {
            if( (cm==CMNone) || (cm==CMPerMesh) ) {
                if (nm==NMPerVert)
                    glEnableClientState (GL_NORMAL_ARRAY);
                glEnableClientState (GL_VERTEX_ARRAY);

                if (nm==NMPerVert)
                    glNormalPointer(GL_FLOAT,sizeof(typename MESH_TYPE::VertexType),&(m->vert.begin()->N()[0]));
                glVertexPointer(3,GL_FLOAT,sizeof(typename MESH_TYPE::VertexType),&(m->vert.begin()->P()[0]));

                glDrawElements(GL_TRIANGLES ,m->fn*3,GL_UNSIGNED_INT, &(*indices.begin()) );
                glDisableClientState (GL_VERTEX_ARRAY);
                if (nm==NMPerVert)
                    glDisableClientState (GL_NORMAL_ARRAY);

                return;
            }
        } else

            if(curr_hints&HNUseTriStrip) {
                //if( (nm==NMPerVert) && ((cm==CMNone) || (cm==CMPerMesh)))
                //	if(curr_hints&HNUseVArray){
                //		glEnableClientState (GL_NORMAL_ARRAY  );
                //		glNormalPointer(GL_FLOAT,sizeof(MESH_TYPE::VertexType),&(m->vert[0].cN()));
                //		glEnableClientState (GL_VERTEX_ARRAY);
                //		glVertexPointer(3,GL_FLOAT,sizeof(MESH_TYPE::VertexType),&(m->vert[0].cP()));
                //		std::vector<GLAElem>::iterator vi;
                //		for(vi=TStripVED.begin();vi!=TStripVED.end();++vi)
                //					glDrawElements(vi->glmode ,vi->len,GL_UNSIGNED_SHORT,&TStripVEI[vi->start] );
                //
                //		glDisableClientState (GL_NORMAL_ARRAY  );
                //		glDisableClientState (GL_VERTEX_ARRAY);
                //		return;
                //	}

                //std::vector< MESH_TYPE::VertexType *>::iterator vi;
                //glBegin(GL_TRIANGLE_STRIP);
                //if(nm == NMPerFace) fip=TStripF.begin();

                //for(vi=TStrip.begin();vi!=TStrip.end(); ++vi){
                //	if((*vi)){
                //		if(nm==NMPerVert) glNormal((*vi)->cN());
                //		if(nm==NMPerFace) glNormal((*fip)->cN());
                //		glVertex((*vi)->P());
                //		}
                //	else
                //		{
                //			glEnd();
                //			glBegin(GL_TRIANGLE_STRIP);
                //		}
                //	if(nm == NMPerFace) ++fip;
                //	}
                //glEnd();
            } else {
                if(partial)
                    fp = face_pointers.begin();
                else
                    fi = m->face.begin();

                if(tm==TMPerWedgeMulti) {
                    curtexname=(*fi).WT(0).n();
                    if (curtexname >= 0) {
                        glEnable(GL_TEXTURE_2D);
                        glBindTexture(GL_TEXTURE_2D,TMId[curtexname]);
                    } else {
                        glDisable(GL_TEXTURE_2D);
                    }
                }

                if(tm==TMPerWedge)
                    glEnable(GL_TEXTURE_2D);

                if(tm==TMPerVert && !TMId.empty()) { // in the case of per vertex tex coord we assume that we have a SINGLE texture.
                    curtexname = 0;
                    glEnable(GL_TEXTURE_2D);
                    glBindTexture(GL_TEXTURE_2D,TMId[curtexname]);
                }
                glBegin(GL_TRIANGLES);

                while( (partial)?(fp!=face_pointers.end()):(fi!=m->face.end())) {
                    typename MESH_TYPE::FaceType & f = (partial)?(*(*fp)): *fi;

                    if(!f.IsD()) {
                        if(tm==TMPerWedgeMulti)
                            if(f.WT(0).n() != curtexname) {
                                curtexname=(*fi).WT(0).n();
                                glEnd();

                                if (curtexname >= 0) {
                                    glEnable(GL_TEXTURE_2D);
                                    glBindTexture(GL_TEXTURE_2D,TMId[curtexname]);
                                } else {
                                    glDisable(GL_TEXTURE_2D);
                                }

                                glBegin(GL_TRIANGLES);
                            }

                        if(nm == NMPerFace)	glNormal(f.cN());
                        if(nm == NMPerVert)	glNormal(f.V(0)->cN());
                        if(nm == NMPerWedge)glNormal(f.WN(0));

                        if(cm == CMPerFace)	glColor(f.C());
                        if(cm == CMPerVert)	glColor(f.V(0)->C());
                        if(tm==TMPerVert) glTexCoord(f.V(0)->T().P());
                        if( (tm==TMPerWedge)||(tm==TMPerWedgeMulti) )glTexCoord(f.WT(0).t(0));
                        glVertex(f.V(0)->P());

                        if(nm == NMPerVert)	glNormal(f.V(1)->cN());
                        if(nm == NMPerWedge)glNormal(f.WN(1));
                        if(cm == CMPerVert)	glColor(f.V(1)->C());
                        if(tm==TMPerVert) glTexCoord(f.V(1)->T().P());
                        if( (tm==TMPerWedge)|| (tm==TMPerWedgeMulti)) glTexCoord(f.WT(1).t(0));
                        glVertex(f.V(1)->P());

                        if(nm == NMPerVert)	glNormal(f.V(2)->cN());
                        if(nm == NMPerWedge)glNormal(f.WN(2));
                        if(cm == CMPerVert) glColor(f.V(2)->C());
                        if(tm==TMPerVert) glTexCoord(f.V(2)->T().P());
                        if( (tm==TMPerWedge)|| (tm==TMPerWedgeMulti)) glTexCoord(f.WT(2).t(0));
                        glVertex(f.V(2)->P());
                    }

                    if(partial)
                        ++fp;
                    else
                        ++fi;
                }

                glEnd();

            }
#endif
    }

// A draw wireframe that hides faux edges
    template <NormalMode nm, ColorMode cm>
    void DrawWirePolygonal() {
#ifdef OLDMESHLAB
        typename MESH_TYPE::FaceIterator fi;


        typename FACE_POINTER_CONTAINER::iterator fp;

        typename std::vector<typename MESH_TYPE::FaceType*>::iterator fip;

        if(cm == CMPerMesh)
            glColor(m->C());

        {
            if(partial)
                fp = face_pointers.begin();
            else
                fi = m->face.begin();

            glBegin(GL_LINES);

            while( (partial)?(fp!=face_pointers.end()):(fi!=m->face.end())) {
                typename MESH_TYPE::FaceType & f = (partial)?(*(*fp)): *fi;

                if(!f.IsD()) {

                    if(nm == NMPerFace)	glNormal(f.cN());
                    if(cm == CMPerFace)	glColor(f.C());

                    if (!f.IsF(0)) {
                        if(nm == NMPerVert)	glNormal(f.V(0)->cN());
                        if(nm == NMPerWedge)glNormal(f.WN(0));
                        if(cm == CMPerVert)	glColor(f.V(0)->C());
                        glVertex(f.V(0)->P());

                        if(nm == NMPerVert)	glNormal(f.V(1)->cN());
                        if(nm == NMPerWedge)glNormal(f.WN(1));
                        if(cm == CMPerVert)	glColor(f.V(1)->C());
                        glVertex(f.V(1)->P());
                    }

                    if (!f.IsF(1)) {
                        if(nm == NMPerVert)	glNormal(f.V(1)->cN());
                        if(nm == NMPerWedge)glNormal(f.WN(1));
                        if(cm == CMPerVert)	glColor(f.V(1)->C());
                        glVertex(f.V(1)->P());

                        if(nm == NMPerVert)	glNormal(f.V(2)->cN());
                        if(nm == NMPerWedge)glNormal(f.WN(2));
                        if(cm == CMPerVert)	glColor(f.V(2)->C());
                        glVertex(f.V(2)->P());
                    }

                    if (!f.IsF(2)) {
                        if(nm == NMPerVert)	glNormal(f.V(2)->cN());
                        if(nm == NMPerWedge)glNormal(f.WN(2));
                        if(cm == CMPerVert)	glColor(f.V(2)->C());
                        glVertex(f.V(2)->P());

                        if(nm == NMPerVert)	glNormal(f.V(0)->cN());
                        if(nm == NMPerWedge)glNormal(f.WN(0));
                        if(cm == CMPerVert)	glColor(f.V(0)->C());
                        glVertex(f.V(0)->P());
                    }

                }

                if(partial)
                    ++fp;
                else
                    ++fi;
            }

            glEnd();

        }
#endif
    }

/// Basic Point drawing fucntion
// works also for mesh with deleted vertices
    template<NormalMode nm, ColorMode cm>
    void DrawPointsBase() {
        typename MESH_TYPE::VertexIterator vi;
        glBegin(GL_POINTS);
        if(cm==CMPerMesh) glColor(m->C());

        for(vi=m->vert.begin(); vi!=m->vert.end(); ++vi)if(!(*vi).IsD()) {
                if(nm==NMPerVert) glNormal((*vi).cN());
                if(cm==CMPerVert) glColor((*vi).C());
                glVertex((*vi).P());
            }
        glEnd();
    }

/// Utility function that computes in eyespace the current distance between the camera and the center of the bbox of the mesh
    double CameraDistance() {
        Point3<typename MESH_TYPE::ScalarType> res;
        Matrix44<typename MESH_TYPE::ScalarType> mm;
        glGetv(GL_MODELVIEW_MATRIX,mm);
        Point3<typename MESH_TYPE::ScalarType>  c=m->bbox.Center();
        res=mm*c;
        return Norm(res);
    }
    template<NormalMode nm, ColorMode cm>
    void DrawPoints() {
#if OLDMESHLAB
        glPushAttrib(GL_ENABLE_BIT | GL_POINT_BIT);
        if(GetHintParami(HNPPointSmooth)>0) glEnable(GL_POINT_SMOOTH);
        else glDisable(GL_POINT_SMOOTH);
        glPointSize(GetHintParamf(HNPPointSize));
        if (glPointParameterfv) {
            if(GetHintParami(HNPPointDistanceAttenuation)>0) {
                float camDist = (float)CameraDistance();
                float quadratic[] = { 0.0f, 0.0f, 1.0f/(camDist*camDist) , 0.0f };
                glPointParameterfv( GL_POINT_DISTANCE_ATTENUATION, quadratic );
                glPointParameterf( GL_POINT_SIZE_MAX, 16.0f );
                glPointParameterf( GL_POINT_SIZE_MIN, 1.0f );
            } else {
                float quadratic[] = { 1.0f, 0.0f, 0.0f};
                glPointParameterfv( GL_POINT_DISTANCE_ATTENUATION, quadratic );
                glPointSize(GetHintParamf(HNPPointSize));
            }
        }

        if(m->vn!=(int)m->vert.size()) {
            DrawPointsBase<nm,cm>();
        } else {
            if(cm==CMPerMesh)
                glColor(m->C());

            // Perfect case, no deleted stuff,
            // draw the vertices using vertex arrays
            if (nm==NMPerVert) {
                glEnableClientState (GL_NORMAL_ARRAY);
                if (m->vert.size() != 0)
                    glNormalPointer(GL_FLOAT,sizeof(typename MESH_TYPE::VertexType),&(m->vert.begin()->N()[0]));
            }
            if (cm==CMPerVert) {
                glEnableClientState (GL_COLOR_ARRAY);
                if (m->vert.size() != 0)
                    glColorPointer(4,GL_UNSIGNED_BYTE,sizeof(typename MESH_TYPE::VertexType),&(m->vert.begin()->C()[0]));
            }

            glEnableClientState (GL_VERTEX_ARRAY);
            if (m->vert.size() != 0)
                glVertexPointer(3,GL_FLOAT,sizeof(typename MESH_TYPE::VertexType),&(m->vert.begin()->P()[0]));

            glDrawArrays(GL_POINTS,0,m->vn);

            glDisableClientState (GL_VERTEX_ARRAY);
            if (nm==NMPerVert)  glDisableClientState (GL_NORMAL_ARRAY);
            if (cm==CMPerVert)  glDisableClientState (GL_COLOR_ARRAY);
        }
        glPopAttrib();
        
#endif
        return;
    }

    void DrawHidden() {
        //const float ZTWIST=HNParamf[HNPZTwist];
        glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_LIGHTING_BIT );
        glEnable(GL_POLYGON_OFFSET_FILL);
        glPolygonOffset(1.0, 1);
        //glDepthRange(ZTWIST,1.0f);
        glDisable(GL_LIGHTING);
        glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
        DrawFill<NMNone,CMNone,TMNone>();
        glDisable(GL_POLYGON_OFFSET_FILL);
        glEnable(GL_LIGHTING);
        glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
        //glDepthRange(0.0f,1.0f-ZTWIST);
        DrawWire<NMPerVert,CMNone>();
        glPopAttrib();
//	glDepthRange(0,1.0f);
    }

    template <NormalMode nm, ColorMode cm, TextureMode tm>
    void DrawFlatWire() {
        //const float ZTWIST=HNParamf[HNPZTwist];
        //glDepthRange(ZTWIST,1.0f);
        glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_LIGHTING_BIT );
        glEnable(GL_POLYGON_OFFSET_FILL);
        glPolygonOffset(1.0, 1);
        DrawFill<nm,cm,tm>();
        glDisable(GL_POLYGON_OFFSET_FILL);
        //glDepthRange(0.0f,1.0f-ZTWIST);
        glEnable(GL_COLOR_MATERIAL);
        glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);
        //glColorMaterial(GL_FRONT,GL_DIFFUSE);
        glColor3f(.3f,.3f,.3f);
        DrawWire<nm,CMNone>();
        glPopAttrib();
        //glDepthRange(0,1.0f);
    }

    template <NormalMode nm, ColorMode cm>
    void DrawRadar() {
        const float ZTWIST=HNParamf[HNPZTwist];
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glDepthMask(0);
        glDepthRange(ZTWIST,1.0f);

        if (cm == CMNone)
            glColor4f(0.2f, 1.0f, 0.4f, 0.2f);
//	DrawFill<nm,cm,TMNone>();
        Draw<DMFlat,CMNone,TMNone>();

        glDepthMask(1);
        glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
//	DrawFill<nm,cm,TMNone>();
        Draw<DMFlat,CMNone,TMNone>();

        glDepthRange(0.0f,1.0f-ZTWIST);
        glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
        glColor4f(0.1f, 1.0f, 0.2f, 0.6f);
        Draw<DMWire,CMNone,TMNone>();
        glDisable(GL_BLEND);
        glDepthRange(0,1.0f);

    }



#ifdef GL_TEXTURE0_ARB
// Multitexturing nel caso voglia usare due texture unit.
    void DrawTexture_NPV_TPW2() {
        unsigned int texname=(*(m->face.begin())).WT(0).n(0);
        glBindTexture(GL_TEXTURE_2D,TMId[texname]);
        typename MESH_TYPE::FaceIterator fi;
        glBegin(GL_TRIANGLES);
        for(fi=m->face.begin(); fi!=m->face.end(); ++fi)if(!(*fi).IsD()) {
                if(texname!=(*fi).WT(0).n(0))	{
                    texname=(*fi).WT(0).n(0);
                    glEnd();
                    glBindTexture(GL_TEXTURE_2D,TMId[texname]);
                    glBegin(GL_TRIANGLES);
                }
                glMultiTexCoordARB(GL_TEXTURE0_ARB, (*fi).WT(0).t(0));
                glMultiTexCoordARB(GL_TEXTURE1_ARB, (*fi).WT(0).t(0));
                glNormal((*fi).V(0)->N());
                glVertex((*fi).V(0)->P());

                glMultiTexCoordARB(GL_TEXTURE0_ARB, (*fi).WT(1).t(0));
                glMultiTexCoordARB(GL_TEXTURE1_ARB, (*fi).WT(1).t(0));
                glNormal((*fi).V(1)->N());
                glVertex((*fi).V(1)->P());

                glMultiTexCoordARB(GL_TEXTURE0_ARB, (*fi).WT(2).t(0));
                glMultiTexCoordARB(GL_TEXTURE1_ARB, (*fi).WT(2).t(0));
                glNormal((*fi).V(2)->N());
                glVertex((*fi).V(2)->P());
            }
        glEnd();
    }

#endif


    /*int MemUsed()
    {
    	int tot=sizeof(GlTrimesh);
    	tot+=sizeof(mesh_type::edge_type)*edge.size();
    	tot+=sizeof(MESH_TYPE::VertexType *) * EStrip.size();
    	tot+=sizeof(MESH_TYPE::VertexType *) * TStrip.size();
    	tot+=sizeof(MESH_TYPE::FaceType *)   * TStripF.size();
    	return tot;
    }*/

private:

    template <NormalMode nm, ColorMode cm>
    void DrawWire() {
#if OLDMESHLAB
        //if(!(curr_hints & (HNUseEdgeStrip | HNUseLazyEdgeStrip) ) )
        if ( (curr_hints & HNIsPolygonal) ) {
            DrawWirePolygonal<nm,cm>();
        } else {
            glPushAttrib(GL_POLYGON_BIT);
            glPolygonMode(GL_FRONT_AND_BACK ,GL_LINE);
            DrawFill<nm,cm,TMNone>();
            glPopAttrib();
        }
        if(m->fn==0 && m->en>0) {
            glPushAttrib(GL_ENABLE_BIT);
            glDisable(GL_LIGHTING);
            glBegin(GL_LINES);
            for(typename mesh_type::EdgeIterator ei=m->edge.begin(); ei!=m->edge.end(); ++ei) {
                glVertex((*ei).V(0)->P());
                glVertex((*ei).V(1)->P());
            }
            glEnd();
            glPopAttrib();
        }
        //	{
//			if(!HasEdges()) ComputeEdges();

        //if(cm==CMPerMesh)	glColor(m->C());
        //std::vector< MESH_TYPE::VertexType *>::iterator vi;
        //glBegin(GL_LINE_STRIP);
        //for(vi=EStrip.begin();vi!=EStrip.end(); ++vi){
        //	if((*vi)){
        //			glNormal((*vi)->N());
        //			glVertex((*vi)->P());
        //		}
        //	else
        //		{
        //			glEnd();
        //			glBegin(GL_LINE_STRIP);
        //		}
        //}
        //glEnd();
        //	}
#endif
    }

    void DrawBBox(ColorMode cm) {
        if(cm==CMPerMesh) glColor(m->C());
        glBoxWire(m->bbox);
    }


};// end class

/*
Crease Angle
Assume che:
la mesh abbia la topologia ff
la mesh non abbia complex (o se li aveva fossero stati detached)
Abbia le normali per faccia normalizzate!!


Prende una mesh e duplica tutti gli edge le cui normali nelle facce incidenti formano un angolo maggiore
di <angle> (espresso in rad).
foreach face
 foreach unvisited vert vi
   scan the star of triangles around vi duplicating vi each time we encounter a crease angle.

the new (and old) vertexes are put in a std::vector that is swapped with the original one at the end.
*/
// uncomment one of the following line to enable the Verbose Trace for Crease
#define VCTRACE (void)0
//#define VCTRACE TRACE

template<class MESH_TYPE>
void Crease(MESH_TYPE &m, typename MESH_TYPE::scalar_type angleRad) {
    assert(m.HasFFTopology());
    typename MESH_TYPE::scalar_type cosangle=Cos(angleRad);

    std::vector<GLW::VertToSplit<MESH_TYPE> > SPL;
    std::vector<typename MESH_TYPE::VertexType> newvert;
    newvert.reserve(m.fn*3);
    // indica se un il vertice z della faccia e' stato processato
    enum {VISITED_0= MESH_TYPE::FaceType::USER0,
          VISITED_1= MESH_TYPE::FaceType::USER0<<1,
          VISITED_2= MESH_TYPE::FaceType::USER0<<2
         } ;
    int vis[3]= {VISITED_0,VISITED_1,VISITED_2};

    //int _t2=clock();
    typename MESH_TYPE::FaceIterator fi;
    for(fi=m.face.begin(); fi!=m.face.end(); ++fi)
        if(!(*fi).IsD())	(*fi).Supervisor_Flags()&= (~(VISITED_0 | VISITED_1 | VISITED_2));

    for(fi=m.face.begin(); fi!=m.face.end(); ++fi)
        if(!(*fi).IsD())
            for(int j=0; j<3; ++j)
                if(!((*fi).Supervisor_Flags() & (vis[j]))) {
                    //VCTRACE("Face %i Spinning around vertex %i\n",fi-m.face.begin(), (*fi).V(j)-m.vert.begin());
                    //(*fi).Supervisor_Flags() |= vis[j];
                    typename MESH_TYPE::hedgepos_type he(&*fi,j,(*fi).V(j));
                    typename MESH_TYPE::hedgepos_type she=he;
                    typename MESH_TYPE::face_base_pointer nextf;
                    GLW::VertToSplit<MESH_TYPE>  spl;
                    spl.newp=false;
                    spl.edge=-1;

                    //Primo giro per trovare un bordo da cui partire
                    do {
                        he.FlipF();
                        he.FlipE();
                        if(he.IsBorder()) break;
                    }	while(he!=she);
                    if(he==she) { // non c'e'bordi allora si cerca un crease
                        do {
                            he.FlipF();
                            he.FlipE();
                            nextf=he.f->F(he.z);
                            typename MESH_TYPE::scalar_type ps=nextf->N()*he.f->N();
                            if(ps<cosangle)   break;
                            int vz=0;
                            if(he.v == he.f->V(he.z)) vz=he.z;
                            if(he.v == he.f->V((he.z+1)%3)) vz=(he.z+1)%3;
                            assert((he.f->Supervisor_Flags() & vis[vz] )==0);
                        }	while(he!=she);
                    }
                    he.FlipE();

                    she=he;
                    newvert.push_back(*(*fi).V(j));
                    typename MESH_TYPE::vertex_pointer curvert=&newvert.back();
//				VCTRACE("Starting from face %i edge %i vert %i \n",he.f-m.face.begin(), he.z, he.v-m.vert.begin());

                    // Secondo giro in cui si riempie il vettore SPL con tutte le info per fare i nuovi vertici
                    do {
                        //TRACE("     -- spinning face %i edge %i vert %i\n",he.f-m.face.begin(), he.z, he.v-m.vert.begin());
                        spl.v=curvert;
                        spl.f=he.f;
                        spl.z=-1;
                        if(he.v == he.f->V(he.z)) spl.z=he.z;
                        if(he.v == he.f->V((he.z+1)%3)) spl.z=(he.z+1)%3;
                        assert(spl.z>=0);
                        //VCTRACE("     -- spinning face vert %i Adding spl face %i vert %i\n",
                        //	he.v-m.vert.begin(),	spl.f-m.face.begin(), spl.z );
                        assert((spl.f->Supervisor_Flags() & vis[spl.z] )==0);
                        spl.f->Supervisor_Flags() |= vis[spl.z];
                        SPL.push_back(spl);
                        spl.newp=false;
                        spl.edge=-1;
                        if(he.IsBorder()) break;
                        nextf=he.f->F(he.z);
                        if(nextf==she.f) break;
                        typename MESH_TYPE::scalar_type ps=nextf->N()*he.f->N();
                        if(ps<cosangle) {
//									VCTRACE("splitting faces %i-%i edge %i vert %i\n",nextf-m.face.begin(),he.f-m.face.begin(), he.z, he.v-m.vert.begin());
                            newvert.push_back(*(he.v));
                            curvert=&newvert.back();
                            spl.newp=true;
                            //spl.edge=he.z;
                        }
                        he.FlipF();
                        if(spl.newp) spl.edge=he.z;
                        he.FlipE();

                    } while(he!=she);
                }
    assert(SPL.size()==m.fn*3);

    typename std::vector<GLW::VertToSplit<MESH_TYPE> >::iterator vsi;
    for(vsi=SPL.begin(); vsi!=SPL.end(); ++vsi) {
        (*vsi).f->V((*vsi).z)=(*vsi).v;
        if((*vsi).newp) {
            assert((*vsi).edge>=0 && (*vsi).edge<3);
            if(!(*vsi).f->IsBorder( (*vsi).edge) )
                (*vsi).f->Detach((*vsi).edge);

        }
    }

    m.vert.math::Swap(newvert);
    m.vn=m.vert.size();
}


} // end namespace

#endif
